package com.yl.module.file.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.yl.common.config.minio.MinioProperties;
import com.yl.common.config.minio.MinioTemplate;
import com.yl.common.constant.Constants;
import com.yl.common.core.domain.message.MessageInfo;
import com.yl.common.exception.ServiceException;
import com.yl.common.properties.SystemProperties;
import com.yl.common.utils.NumberUtils;
import com.yl.common.utils.StringUtils;
import com.yl.framework.web.service.WebSocketJiankeSever;
import com.yl.module.file.domain.File;
import com.yl.module.file.domain.FileType;
import com.yl.module.file.domain.vo.File2VO;
import com.yl.module.file.domain.vo.FileVO;
import com.yl.module.file.mapper.FileMapper;
import com.yl.module.file.service.FileService;
import com.yl.module.file.service.FileTypeService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.rmi.ServerException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;

/**
 * 文件表 Service实现类
 * @author liangjian
 * @date 2023-11-24 16:52:24
 */
@Service
@Slf4j
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {

	@Autowired
	FileMapper fileMapper;
	@Autowired
	public SystemProperties systemProperties;
	@Autowired
	public MinioTemplate minioTemplate;
	@Autowired
	public MinioProperties minioProperties;
	@Autowired
	public FileTypeService fileTypeService;
	private final Tika tika = new Tika();

	/**
	 * 测试
	 */
	@Override
	public void test()  {

		// copy2(Lists.newArrayList(10L,18L),0);

		// minioTemplate.copy(
		// 		"dev",
		// 		"work/knowledge/attachment/f83cbde5-ae8b-4a94-bb23-31734c88263a.png",
		// 		"dev",
		// 		"work/knowledge/attachment/111.png"
		// );

		// List<File2VO> files = get(110000, "2");
		// System.out.println(JSONUtil.toJsonStr(files));

		// updSave(110101,10L,Lists.newArrayList(10L));

		// Map<Integer, List<File2VO>> map = getMap(110101, Lists.newArrayList(1, 2));


		// getMap(Lists.newArrayList(
		// 		new FileGetMapDTO(1,10),
		// 		new FileGetMapDTO(1,10),
		// 		new FileGetMapDTO(2,10),
		// 		new FileGetMapDTO(2,10),
		// 		new FileGetMapDTO(1,20),
		// 		new FileGetMapDTO(1,20),
		// 		new FileGetMapDTO(3,30L),
		// 		new FileGetMapDTO(3,30L),
		// 		new FileGetMapDTO(4,"40"),
		// 		new FileGetMapDTO(4,"40")
		// ));

		getMap(
				Lists.newArrayList(1,2),
				Lists.newArrayList(10,20)
		);

		// getMap(
		// 		Lists.newArrayList(1,2,3,4,5),
		// 		Lists.newArrayList(10,20,30L,"40")
		// );

		System.out.println("111111111111111111111");
	}

	/**
	 * 上传 测试
	 */
	@Override
	public FileVO uploadTest(MultipartFile file) throws IOException {
		if(Objects.isNull(file)){
			throw new ServerException("没有选择文件");
		}

		String contentType = file.getContentType();
		//获取后缀
		String fileName = file.getOriginalFilename();
		String suffix = fileName.substring(fileName.indexOf(".")+1);


		String detect = tika.detect(file.getInputStream());
		System.out.println(detect);
		System.out.println(contentType);
		System.out.println(fileName);
		System.out.println(suffix);

		// minioTemplate.putMultipartFile("dev","user/head/"+fileName,file);
		/*
		文件访问链接 http://localhost:48000/dev/user/head/1.png
		此访问链接是nginx的 在nginx中代理到了minio
		nginx的代理配置如下
		server {
			listen       48000;
			server_name  127.0.0.1;
			location / {
				#代理到minio
				proxy_pass http://127.0.0.1:9000/;
			}
		}
		*/
		return null;
	}

	/**
	 * 下载 测试
	 */
	@Override
	public void downLoadTest(HttpServletResponse response,String fileName)  {
		InputStream in = minioTemplate.getObject("dev", "user/head/"+fileName);
		try {
			IoUtil.copy(in, response.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
			log.error("下载失败: ", e);
		} finally {
			IoUtil.close(in);
		}
	}

	/**
	 * 上传
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	@SneakyThrows
	public File2VO upload(Integer fileTypeId, MultipartFile file){
		if(Objects.isNull(file)){
			throw new ServerException("文件不能为空");
		}
		//获取文件名、文件后缀
		String fileName = file.getOriginalFilename();
		String suffix = getFileSuffix(fileName);
		//获取文件类型
		FileType fileType = getFileType(fileTypeId);
		//文件数据保存到表file中
		File fileSave = fileSave(fileType,fileName,suffix, Constants.YES);
		//上传到minio
		minioTemplate.putMultipartFile(
				fileSave.getBucket(),
				fileSave.getUrl()+"/"+fileSave.getName()+"."+fileSave.getSuffix(),
				file
		);
		//组装访问文件访问url数据 并返回给前端
		File2VO vo = new File2VO(
				fileSave.getId(),
				fileName,
				createUrl(fileSave),
				suffix
		);

		return vo;
	}

	/**
	 * 通过文件名获取文件后缀
	 * @param fileName
	 * @return String 文件后缀
	 */
	private String getFileSuffix(String fileName){
		if (StrUtil.isBlank(fileName)) {
			throw new ServiceException("文件名异常");
		}

		if (fileName.lastIndexOf(".") == -1) {
			throw new ServiceException("上传的文件必须有文件后缀");// 无扩展名的情况
		}
		fileName = fileName.toLowerCase();
		String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
		if(!File.fileSuffix.contains(suffix)){
			throw new ServiceException("无效文件，当前文件后缀为"+suffix+"，只能上传后缀为"+File.fileSuffix+"的文件。");
		}

		return suffix;
	}

	/**
	 * 获取文件业务类型
	 * @param fileTypeId 文件业务类型id 参照表file_type
	 * @return FileType
	 */
	private FileType getFileType(Integer fileTypeId){
		if(NumberUtils.integerEmpty(fileTypeId)){
			throw new ServiceException("文件业务类型fileTypeId不能为空");
		}

		FileType fileType = fileTypeService.getById(fileTypeId);
		if(Objects.isNull(fileType)){
			throw new ServiceException("未查询到此文件业务类型，请重新确认fileTypeId参数");
		}
		return fileType;
	}

	/**
	 * 文件数据保存到表file中
	 * @param fileType 文件业务类型对象
	 * @param fileName 文件名称
	 * @param suffix 文件后缀
	 * @param deleteFlag 删除标准 0未删除 1删除
	 * @return FileType
	 */
	private File fileSave(
			FileType fileType,
			String fileName,
			String suffix,
			Integer deleteFlag
	){
		File file = new File();
		file.setEnterType(File.EnumEnterType.HAND_MOVEMENT.getEnterType());
		file.setFileTypeId(fileType.getId());
		file.setOriginalName(fileName);
		file.setName(UUID.randomUUID().toString());
		file.setSuffix(suffix);
		file.setBucket(minioProperties.getBucketName());
		file.setUrl(fileType.getCodes());
		file.setDeleteFlag(deleteFlag);
		this.save(file);

		return file;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@SneakyThrows
	public File2VO upload(Integer fileTypeId, java.io.File file){
		//获取文件名、文件后缀
		String fileName = file.getName();
		String suffix = getFileSuffix(fileName);
		//获取文件类型
		FileType fileType = getFileType(fileTypeId);
		//文件数据保存到表file中
		File fileSave = fileSave(fileType,fileName,suffix,Constants.YES);
		//上传到minio
		minioTemplate.putFile(
				fileSave.getBucket(),
				fileSave.getUrl()+"/"+fileSave.getName()+"."+fileSave.getSuffix(),
				file,
				tika.detect(file)
		);
		//组装访问文件访问url数据 并返回给前端
		File2VO vo = new File2VO(
				fileSave.getId(),
				fileName,
				createUrl(fileSave),
				suffix
		);
		return vo;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@SneakyThrows
	public File2VO upload(
		byte[] bytes,
		Integer fileTypeId,
		String fileName,
		Integer deleteFlag
	){
		//获取文件名、文件后缀
		String suffix = getFileSuffix(fileName);
		//获取文件类型
		FileType fileType = getFileType(fileTypeId);
		//文件数据保存到表file中
		File fileSave = fileSave(fileType,fileName,suffix,deleteFlag);
		//上传到minio
		minioTemplate.putBytes(
				fileSave.getBucket(),
				fileSave.getUrl()+"/"+fileSave.getName()+"."+fileSave.getSuffix(),
				bytes,
				File.SuffixEnum.map.get(fileSave.getSuffix()).getType()
		);
		//组装访问文件访问url数据 并返回给前端
		File2VO vo = new File2VO(
				fileSave.getId(),
				fileName,
				createUrl(fileSave),
				suffix
		);
		return vo;
	}

	/**
	 * 生成网络可访问的url
	 * @param fileId 我们业务系统文件id
	 * @return String 文件网络可访问的url
	 */
	@Override
	public String createUrl(Long fileId){
		if(NumberUtils.longEmpty(fileId)){
			throw new ServiceException("文件id不能为空");
		}

		File file = this.getById(fileId);
		if(Objects.isNull(file)){
			throw new ServiceException("没有找到文件");
		}

		return createUrl(file);
	}

	/**
	 * 生成网络可访问的url
	 * @param file 我们业务系统文件File对象
	 * @return String 文件网络可访问的url
	 */
	@Override
	public String createUrl(File file){
		if(Objects.isNull(file)){
			throw new ServiceException("参数file不能为空");
		}

		StringBuilder sb = new StringBuilder();
		sb.append(minioProperties.getFileDomainName()).append("/")//http://192.168.2.44:48000/
				.append(file.getBucket()).append("/")//http://192.168.2.44:48000/dev/
				.append(file.getUrl()+"/"+file.getName()+"."+file.getSuffix());//http://192.168.2.44:48000/dev/test/1.jpg
		String url = sb.toString();

		return url;
	}


	/**
	 * 文件查询
	 * @param fileTypeId 文件类型id 来源file_type.id
	 * @param relevanceId 文件关联的业务id 例如工单id 用户id
	 * @return List<File2VO>
	 */
	@Override
	public <T> List<File2VO> get(Integer fileTypeId,T relevanceId){
		if(NumberUtils.integerEmpty(fileTypeId)||Objects.isNull(relevanceId)){
			throw new ServiceException("参数异常");
		}

		LambdaQueryWrapper<File> lqw = new LambdaQueryWrapper<>();
		if (relevanceId instanceof Integer) {
			lqw.eq(File::getRelevanceIntId,relevanceId);
		}else if (relevanceId instanceof Long) {
			lqw.eq(File::getRelevanceLongId,relevanceId);
		}else if (relevanceId instanceof String) {
			lqw.eq(File::getRelevanceStrId,relevanceId);
		}else{
			throw new ServiceException("没有匹配到类型");
		}
		lqw.eq(File::getFileTypeId,fileTypeId);
		lqw.eq(File::getDeleteFlag,Constants.NO);

		List<File> list = this.list(lqw);
		if(CollectionUtils.isEmpty(list)){
			return new ArrayList<>();
		}

		List<File2VO> result = new ArrayList<>();
		for(File i:list){
			String accessUrl = createUrl(i);
			result.add(new File2VO(i.getId(),i.getOriginalName(),accessUrl,i.getSuffix()));
		}

		return result;
	}

	@Override
	public <T> Map<T,List<File2VO>> getMap(Integer fileTypeId,List<T> relevanceIds){
		Map<T,List<File2VO>> map = new HashMap<>();
		if(NumberUtils.integerEmpty(fileTypeId)||CollectionUtils.isEmpty(relevanceIds)){
			return map;
		}

		for(T i:relevanceIds){
			if(Objects.isNull(i)||i.toString().equals("")||i.toString().equals("0")){
				throw new ServiceException("参数relevanceIds中的值存在空值");
			}
		}

		T relevanceId = relevanceIds.get(0);
		LambdaQueryWrapper<File> lqw = new LambdaQueryWrapper<>();
		if (relevanceId instanceof Integer) {
			lqw.in(File::getRelevanceIntId,relevanceIds);
		}else if (relevanceId instanceof Long) {
			lqw.in(File::getRelevanceLongId,relevanceIds);
		}else if (relevanceId instanceof String) {
			lqw.in(File::getRelevanceStrId,relevanceIds);
		}else{
			throw new ServiceException("没有匹配到类型");
		}
		lqw.eq(File::getFileTypeId,fileTypeId);
		lqw.eq(File::getDeleteFlag,Constants.NO);

		List<File> fileList = this.list(lqw);
		if(CollectionUtils.isEmpty(fileList)){
			return new HashMap<>();
		}


		for(File i:fileList){
			T t = null;
			if (relevanceId instanceof Integer) {
				t = (T)i.getRelevanceIntId();
			}else if (relevanceId instanceof Long) {
				t = (T)i.getRelevanceLongId();
			}else if (relevanceId instanceof String) {
				t = (T)i.getRelevanceStrId();
			}

			if(!map.containsKey(t)){
				map.put(t,new ArrayList<>());
			}

			List<File2VO> list = map.get(t);
			String accessUrl = createUrl(i);
			list.add(new File2VO(i.getId(),i.getOriginalName(),accessUrl));
		}

		return map;
	}

	@Override
	public Map<String,List<File2VO>> getMap(List<Integer> fileTypeIds,List<Object> relevanceIds){
		Map<String,List<File2VO>> map = new HashMap<>();
		if(CollectionUtils.isEmpty(fileTypeIds)||CollectionUtils.isEmpty(relevanceIds)){
			return map;
		}
		for(Integer i:fileTypeIds){
			if(NumberUtils.integerEmpty(i)){
				throw new ServiceException("参数fileTypeIds中不能存在空值");
			}
		}

		Set<Integer> intRelevanceIds = new HashSet<>();
		Set<Long> longRelevanceIds = new HashSet<>();
		Set<String> strRelevanceIds = new HashSet<>();
		for(Object i:relevanceIds){
			if(
				Objects.isNull(i)||
				i.toString().equals("")||
				i.toString().equals("0")
			){
				throw new ServiceException("参数relevanceIds中不能存在空值");
			}

			if (i instanceof Integer) {
				intRelevanceIds.add((Integer) i);
			}else if (i instanceof Long) {
				longRelevanceIds.add((Long) i);
			}else if (i instanceof String) {
				strRelevanceIds.add((String) i);
			}else{
				throw new ServiceException("没有匹配到类型");
			}
		}

		// intRelevanceIds = new HashSet<>();
		// longRelevanceIds = new HashSet<>();
		// strRelevanceIds = new HashSet<>();

		List<File> files = fileMapper.getByFileTypesIds(
				fileTypeIds,
				new ArrayList<>(intRelevanceIds),
				new ArrayList<>(longRelevanceIds),
				new ArrayList<>(strRelevanceIds)
		);
		if(CollectionUtils.isEmpty(files)){
			return new HashMap<>();
		}

		for(File i:files){
			Integer fileTypeId = i.getFileTypeId();
			Integer relevanceIntId = i.getRelevanceIntId();
			Long relevanceLongId = i.getRelevanceLongId();
			String relevanceStrId = i.getRelevanceStrId();

			String key = "";
			if(!NumberUtils.integerEmpty(relevanceIntId)){
				key = fileTypeId+"-"+relevanceIntId;
			}else if(!NumberUtils.longEmpty(relevanceLongId)){
				key = fileTypeId+"-"+relevanceLongId;
			}else if(StringUtils.isNotEmpty(relevanceStrId)){
				key = fileTypeId+"-"+relevanceStrId;
			}

			if (!map.containsKey(key)) {
				map.put(key,new ArrayList<>());
			}

			List<File2VO> file2VOS = map.get(key);
			String url = createUrl(i);
			file2VOS.add(new File2VO(i.getId(),i.getOriginalName(),url));
		}

		return map;
	}

	/**
	 * 新增文件保存
	 * @param relevanceId 文件关联的业务id 例如工单id 用户id等
	 * @param fileId 文件id
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public <T> void addSave(T relevanceId,Long fileId){

		if(Objects.isNull(relevanceId)||NumberUtils.longEmpty(fileId)){
			throw new ServiceException("参数relevanceId、fileId都不能为空");
		}

		File file = new File();
		file.setId(fileId);
		if (relevanceId instanceof Integer) {
			file.setRelevanceIntId((Integer) relevanceId);
		}else if (relevanceId instanceof Long) {
			file.setRelevanceLongId((Long) relevanceId);
		}else if (relevanceId instanceof String) {
			file.setRelevanceStrId((String) relevanceId);
		}else{
			throw new ServiceException("没有匹配到类型");
		}
		file.setDeleteFlag(Constants.NO);

		this.updateById(file);
	}

	/**
	 * 新增文件保存
	 * @param relevanceId 文件关联的业务id 例如工单id 用户id等
	 * @param fileIds 文件id集
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public <T> void addSave(T relevanceId,List<Long> fileIds){

		if(Objects.isNull(relevanceId)||CollectionUtils.isEmpty(fileIds)){
			throw new ServiceException("参数relevanceId、fileIds都不能为空");
		}

		List<File> files = new ArrayList<>();
		for(Long i:fileIds){
			File file = new File();
			file.setId(i);
			if (relevanceId instanceof Integer) {
				file.setRelevanceIntId((Integer) relevanceId);
			}else if (relevanceId instanceof Long) {
				file.setRelevanceLongId((Long) relevanceId);
			}else if (relevanceId instanceof String) {
				file.setRelevanceStrId((String) relevanceId);
			}else{
				throw new ServiceException("没有匹配到类型");
			}
			file.setDeleteFlag(Constants.NO);
			files.add(file);
		}
		this.updateBatchById(files);
	}

	/**
	 * 修改文件保存
	 * @param fileTypeId 文件类型id 来源file_type.id
	 * @param relevanceId 文件关联的业务id 例如工单id 用户id等
	 * @param fileIds 文件id
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public <T> void updSave(Integer fileTypeId,T relevanceId,List<Long> fileIds){

		if(
			NumberUtils.integerEmpty(fileTypeId)||
			Objects.isNull(relevanceId)||
			relevanceId.toString().equals("0")||
			relevanceId.toString().equals("")
		){
			throw new ServiceException("参数fileTypeId、relevanceId都不能为空");
		}

		FileType fileType = fileTypeService.getById(fileTypeId);
		if(Objects.isNull(fileType)){
			throw new ServiceException("没有找到对应的文件类型，请重新确认fileTypeId参数");
		}

		//先逻辑删除所有文件
		LambdaUpdateWrapper<File> luw = new LambdaUpdateWrapper<File>();
		luw.set(File::getDeleteFlag,Constants.YES);
		luw.eq(File::getFileTypeId,fileTypeId);
		if (relevanceId instanceof Integer) {
			luw.eq(File::getRelevanceIntId,relevanceId);
		}else if (relevanceId instanceof Long) {
			luw.eq(File::getRelevanceLongId,relevanceId);
		}else if (relevanceId instanceof String) {
			luw.eq(File::getRelevanceStrId,relevanceId);
		}else{
			throw new ServiceException("没有匹配到类型");
		}

		this.update(luw);

		//在添加文件 添加文件是修改文件的状态
		if(!CollectionUtils.isEmpty(fileIds)){
			addSave(relevanceId,fileIds);
		}

	}

	/**
	 * 文件实体File转File2VO
	 * @param files 文件数组
	 * @return List<File2VO>
	 */
	@Override
	public List<File2VO> fileToFile2VO(List<File> files){
		if(CollectionUtils.isEmpty(files)){
			return new ArrayList<>();
		}
		List<File2VO> result = new ArrayList<>();
		for(File i: files){
			result.add(new File2VO(i.getId(),i.getOriginalName(),createUrl(i)));
		}
		return result;
	}

	/**
	 * 定时删除XX时间前的无效文件
	 */


	/**
	 * 定时删除XX时间前的无效文件
	 */
	//每天1点和23点执行无效文件删除
	@Scheduled(cron = "0 0 1 * * ?")
	public void delScheduled(){
		del();
	}
	@Scheduled(cron = "0 0 23 * * ?")
	public void delScheduled2(){
		del();
	}
	@Override
	public void del(){
		//查询XX时间前应该删除的文件
		LambdaQueryWrapper<File> lqw = new LambdaQueryWrapper<>();
		//lqw.le(File::getCreateTime,LocalDateTime.now().minusMonths(1));//一个月前
		lqw.le(File::getCreateTime,LocalDateTime.now().minusDays(7));//一周前
		lqw.le(File::getDeleteFlag,Constants.YES);
		List<File> file = this.list(lqw);
		if (CollectionUtils.isEmpty(file)) {
			return;
		}

		//删除前的数据检查
		file = file.stream()
				.filter(i->!Objects.isNull(i)&&StrUtil.isNotBlank(i.getBucket())&&StrUtil.isNotBlank(i.getUrl())&&StrUtil.isNotBlank(i.getName())&&StrUtil.isNotBlank(i.getSuffix()))
				.collect(Collectors.toList());
		if (CollectionUtils.isEmpty(file)) {
			return;
		}
		//按桶进行分组
		Map<String,List<File>> map = file.stream()
				.collect(Collectors.groupingBy(File::getBucket));

		Map<String,Long> urlFileIdMap = new HashMap<>();
		List<String> delFailFile = new ArrayList<>();
		for(String key:map.keySet()){
			List<File> files = map.get(key);
			List<String> objectNames = new ArrayList<>();

			for(File f:files){
				StringBuilder sb = new StringBuilder();
				sb.append(f.getUrl()).append("/");
				sb.append(f.getName()).append(".");
				sb.append(f.getSuffix());

				String s = sb.toString();
				objectNames.add(sb.toString());
				//key为文件在minio桶中的名称（是一个全路径的文件地址） value为文件id
				urlFileIdMap.put(s,f.getId());
			}

			//执行文件删除 delFailFile是返回删除失败的文件
			delFailFile = minioTemplate.removeObject(key, objectNames);
			for(String i:delFailFile){
				urlFileIdMap.remove(i);
			}

			//执行文件数据删除
			List<Long> delFileIds = urlFileIdMap
					.values()
					.stream()
					.collect(Collectors.toList());
			this.removeBatchByIds(delFileIds);
		}
	}

	/**
	 * 文件逻辑删除
	 * @param fileIds 文件id数组
	 */
	@Override
	public void del(List<Long> fileIds){
		List<File> files = new ArrayList<>();
		for(Long i:fileIds){
			File file = new File();
			file.setId(i);
			file.setDeleteFlag(Constants.YES);
			files.add(file);
		}
		this.updateBatchById(files);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Map<Long,File2VO> copy(Integer fileTypeId,List<Long> fileIds){
		Map<Long,File2VO> map = new HashMap<>();

		if(CollectionUtils.isEmpty(fileIds)){
			return map;
		}

		//通过fileIds查询原文件
		LambdaQueryWrapper<File> lqw = new LambdaQueryWrapper<>();
		lqw.in(File::getId,fileIds);
		List<File> list = this.list(lqw);
		if (CollectionUtils.isEmpty(list)){
			return map;
		}

		//对原文件数据 进行复制克隆一份 在保存
		List<File> listCopy = ObjectUtil.cloneByStream(list);
		for(File i:listCopy){
			i.setId(null);
			if(!NumberUtils.integerEmpty(fileTypeId)){
				i.setFileTypeId(fileTypeId);
			}
			i.setRelevanceIntId(null);
			i.setRelevanceLongId(null);
			i.setRelevanceStrId(null);
			i.setDeleteFlag(Constants.YES);
			i.setName(UUID.randomUUID().toString());
		}
		this.saveBatch(listCopy);

		//在minion中对文件进行复制一份
		for(int i=0;i<list.size();i++){
			File file = list.get(i);
			File fileCopy = listCopy.get(i);

			String sourceBucket = file.getBucket();
			String sourceObject = file.getUrl()+"/"+file.getName()+"."+file.getSuffix();
			String destinationBucket = fileCopy.getBucket();
			String destinationObject = fileCopy.getUrl()+"/"+fileCopy.getName()+"."+fileCopy.getSuffix();

			minioTemplate.copy(sourceBucket,sourceObject,destinationBucket,destinationObject);
			map.put(file.getId(),new File2VO(fileCopy.getId(),fileCopy.getOriginalName(),createUrl(fileCopy)));
		}

		return map;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public List<File2VO> copy2(Integer fileTypeId,List<Long> fileIds){
		Map<Long, File2VO> map = copy(fileTypeId,fileIds);
		List<File2VO> list = map.values().stream()
				.collect(Collectors.toList());
		return list;
	}

	@Override
	public void fileDownload(Integer fileTypeId, String filePath,String fileName,
								 HttpServletResponse response) {
		if (fileTypeId == null || StringUtils.isEmpty(filePath) ) {
			throw  new ServiceException("下载失败！");
		}
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {


			outputStream = response.getOutputStream();
			// 获取文件对象
			inputStream = minioTemplate.getObject(minioProperties.getBucketName(), filePath);
			response.reset();
			response.setHeader("Content-Disposition", "attachment;filename=" +
					URLEncoder.encode(fileName, "UTF-8"));
			response.setContentType("application/octet-stream");
			response.setCharacterEncoding("UTF-8");
			// 输出文件
			IoUtil.copy(inputStream, response.getOutputStream());
			inputStream.close();
		} catch (Exception e) {
			log.error("文件下载失败",e);
		} finally {
			IoUtil.close(inputStream);
			IoUtil.close(outputStream);
		}
	}

	@Override
	public void fileDownload(
			HttpServletResponse response,
			Long fileId,
			String fileName
	) {

		File file = this.getById(fileId);
		if (Objects.isNull(file)) {
			throw new ServiceException("文件不存在");
		}

		if(StrUtil.isBlank(fileName)){
			fileName = file.getOriginalName();
		}else{
			fileName = fileName+"."+file.getSuffix();
		}

		InputStream inputStream = null;
		OutputStream outputStream = null;
		String minioFileUrl = file.getUrl()+"/"+file.getName()+"."+file.getSuffix();
		try {
			outputStream = response.getOutputStream();
			// 获取文件对象
			inputStream = minioTemplate.getObject(minioProperties.getBucketName(),minioFileUrl);

			// 输出文件
			response.reset();
			response.setHeader("Content-Disposition", "attachment;filename=" +
					URLEncoder.encode(fileName, "UTF-8"));
			response.setContentType("application/octet-stream");
			response.setCharacterEncoding("UTF-8");

			IoUtil.copy(inputStream, response.getOutputStream());
			inputStream.close();
		} catch (Exception e) {
			log.error("文件下载失败",e);
		} finally {
			IoUtil.close(inputStream);
			IoUtil.close(outputStream);
		}








	}

}



